Interactive graphics with plotly

install.packages("plotly")

First Interactive Plot

library(plotly)
library(tidyverse)
p <- ggplot(data = midwest) +
  geom_point(mapping = aes(x = popdensity, y = percollege))
ggplotly(p)

Customized Interactive Plot

p <- ggplot(midwest, 
       aes(x = popdensity, y = percollege, color = state)) +
  geom_point() + 
  scale_x_continuous("Population Density", 
                     breaks = seq(0, 80000, 20000)) + 
  scale_y_continuous("Percent College Graduates") + 
  scale_color_discrete("State") + 
  theme_bw()
ggplotly(p)

Your Turn

  1. Using the starwars data, create a static ggplot and use the ggplotly function to turn it interactive.

Lord of the Rings Data

lotr <- read_tsv('https://raw.githubusercontent.com/jennybc/lotr/master/lotr_clean.tsv')
Parsed with column specification:
cols(
  Film = col_character(),
  Chapter = col_character(),
  Character = col_character(),
  Race = col_character(),
  Words = col_integer()
)
lotr

Create plotly by hand

plot_ly(lotr, x = ~Words) %>% add_histogram()

Subplots

one_plot <- function(d) {
  plot_ly(d, x = ~Words) %>%
    add_histogram() %>%
    add_annotations(
      ~unique(Film), x = 0.5, y = 1, 
      xref = "paper", yref = "paper", showarrow = FALSE
    )
}
lotr %>%
  split(.$Film) %>%
  lapply(one_plot) %>% 
  subplot(nrows = 1, shareX = TRUE, titleX = FALSE) %>%
  hide_legend()

Grouped bar plot

plot_ly(lotr, x = ~Race, color = ~Film) %>% add_histogram()

Plot of proportions

# number of diamonds by cut and clarity (n)
lotr_count <- count(lotr, Race, Film)
# number of diamonds by cut (nn)
lotr_prop <- left_join(lotr_count, count(lotr_count, Race, wt = n))
lotr_prop %>%
  mutate(prop = n / nn) %>%
  plot_ly(x = ~Race, y = ~prop, color = ~Film) %>%
  add_bars() %>%
  layout(barmode = "stack")

Your Turn

  1. Using the gss_cat data, create a histrogram for the tvhours variable.
  2. Using the gss_cat data, create a bar chart showing the partyid variable by the marital status.

Scatterplots by Hand

plot_ly(midwest, x = ~popdensity, y = ~percollege) %>%
  add_markers()

Change symbol

plot_ly(midwest, x = ~popdensity, y = ~percollege) %>%
  add_markers(symbol = ~state)

Change color

plot_ly(midwest, x = ~popdensity, y = ~percollege) %>%
  add_markers(color = ~state, colors = viridis::viridis(5))

Line Graph

storms_yearly <- storms %>%
  group_by(year) %>%
  summarise(num = length(unique(name)))
plot_ly(storms_yearly, x = ~year, y = ~num) %>%
  add_lines()

Your Turn

Highcharter; Highcharts for R

devtools::install_github("jbkunst/highcharter")

hchart function

library(highcharter)
Highcharts (www.highcharts.com) is a Highsoft software product which is
not free for commercial and Governmental use
lotr_count <- lotr %>%
  count(Film, Race)
hchart(lotr_count, "column", hcaes(x = Race, y = n, group = Film))

A second hchart

hchart(midwest, "scatter", hcaes(x = popdensity, y = percollege, group = state))

Histogram

hchart(lotr$Words)

Your Turn

Build Highcharts from scratch

hc <- highchart() %>%
  hc_xAxis(categories = lotr_count$Race) %>%
  hc_add_series(name = 'The Fellowship Of The Ring', 
                data = filter(lotr_count, Film == 'The Fellowship Of The Ring')$n) %>% 
  hc_add_series(name = 'The Two Towers', 
                data = filter(lotr_count, Film == 'The Two Towers')$n) %>%
  hc_add_series(name = 'The Return Of The King', 
                data = filter(lotr_count, Film == 'The Return Of The King')$n)
hc

Change Chart type

hc <- hc %>%
  hc_chart(type = 'column')
hc

Change Colors

hc <- hc %>%
  hc_colors(substr(viridis(3), 0, 7))
hc

Modify Axes

hc <- hc %>%
  hc_xAxis(title = list(text = "Race")) %>%
  hc_yAxis(title = list(text = "Number of Words Spoken"),
           showLastLabel = FALSE)
hc

Add title, subtitle, move legend

hc <- hc %>%
  hc_title(text = 'Number of Words Spoken in Lord of the Rings Films',
           align = 'left') %>%
  hc_subtitle(text = 'Broken down by <i>Film</i> and <b>Race</b>', 
              align = 'left') %>%
  hc_legend(align = 'right', verticalAlign = 'top', layout = 'vertical',
            x = 0, y = 80) %>%
  hc_exporting(enabled = TRUE)
hc

Your Turn

Correlation Matrices

select(storms, wind, pressure, ts_diameter, hu_diameter) %>%
  cor(use = "pairwise.complete.obs") %>%
  hchart()

Leaflet Example

library(leaflet)
storms %>%
  filter(name %in% c('Ike', 'Katrina'), year > 2000) %>%
  leaflet() %>%
  addTiles() %>%
  addCircles(lng = ~long, lat = ~lat, popup = ~name, weight = 1,
             radius = ~wind*1000)

Additional Resources

LS0tDQp0aXRsZTogIkRhdGEgVmlzdWFsaXphdGlvbiAtIEludGVyYWN0aXZlIEdyYXBoaWNzIHVzaW5nIFIiDQphdXRob3I6ICJCcmFuZG9uIExlQmVhdSINCmRhdGU6ICJKdW5lIDUsIDIwMTgiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7ciBzZXR1cF9jaHVua3MsIGVjaG8gPSBGQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9NiwgZmlnLmNhcCA9IE5VTEwpIA0KYGBgDQoNCiMgSW50ZXJhY3RpdmUgZ3JhcGhpY3Mgd2l0aCBwbG90bHkNCmBgYHtyIGluc3RhbGwsIGV2YWwgPSBGQUxTRX0NCmluc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQpgYGANCg0KIyBGaXJzdCBJbnRlcmFjdGl2ZSBQbG90DQpgYGB7ciBmaXJzdF9wbG90bHksIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDZ9DQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KcCA8LSBnZ3Bsb3QoZGF0YSA9IG1pZHdlc3QpICsNCiAgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHggPSBwb3BkZW5zaXR5LCB5ID0gcGVyY29sbGVnZSkpDQpnZ3Bsb3RseShwKQ0KYGBgDQoNCiMgQ3VzdG9taXplZCBJbnRlcmFjdGl2ZSBQbG90DQpgYGB7ciBjdXN0b21fcGxvdGx5LCB3YXJuaW5nID0gRkFMU0UsIGZpZy53aWR0aCA9IDksIGZpZy5oZWlnaHQgPSA2fQ0KcCA8LSBnZ3Bsb3QobWlkd2VzdCwgDQogICAgICAgYWVzKHggPSBwb3BkZW5zaXR5LCB5ID0gcGVyY29sbGVnZSwgY29sb3IgPSBzdGF0ZSkpICsNCiAgZ2VvbV9wb2ludCgpICsgDQogIHNjYWxlX3hfY29udGludW91cygiUG9wdWxhdGlvbiBEZW5zaXR5IiwgDQogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBzZXEoMCwgODAwMDAsIDIwMDAwKSkgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKCJQZXJjZW50IENvbGxlZ2UgR3JhZHVhdGVzIikgKyANCiAgc2NhbGVfY29sb3JfZGlzY3JldGUoIlN0YXRlIikgKyANCiAgdGhlbWVfYncoKQ0KZ2dwbG90bHkocCkNCmBgYA0KDQojIFlvdXIgVHVybg0KMS4gVXNpbmcgdGhlIGBzdGFyd2Fyc2AgZGF0YSwgY3JlYXRlIGEgc3RhdGljIGdncGxvdCBhbmQgdXNlIHRoZSBgZ2dwbG90bHlgIGZ1bmN0aW9uIHRvIHR1cm4gaXQgaW50ZXJhY3RpdmUuIA0KMi4gDQoNCiMgTG9yZCBvZiB0aGUgUmluZ3MgRGF0YQ0KLSBEYXRhIGZyb20gSmVubnkgQnJ5YW46IDxodHRwczovL2dpdGh1Yi5jb20vamVubnliYy9sb3RyPg0KDQpgYGB7ciByZWFkX2luX2xvdHIsIGVycm9yID0gRkFMU0V9DQpsb3RyIDwtIHJlYWRfdHN2KCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vamVubnliYy9sb3RyL21hc3Rlci9sb3RyX2NsZWFuLnRzdicpDQpsb3RyDQpgYGANCg0KIyBDcmVhdGUgcGxvdGx5IGJ5IGhhbmQNCmBgYHtyIHBsb3RseV9ieV9oYW5kLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNn0NCnBsb3RfbHkobG90ciwgeCA9IH5Xb3JkcykgJT4lIGFkZF9oaXN0b2dyYW0oKQ0KYGBgDQoNCiMgU3VicGxvdHMNCmBgYHtyIHN1YnBsb3RzLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNn0NCm9uZV9wbG90IDwtIGZ1bmN0aW9uKGQpIHsNCiAgcGxvdF9seShkLCB4ID0gfldvcmRzKSAlPiUNCiAgICBhZGRfaGlzdG9ncmFtKCkgJT4lDQogICAgYWRkX2Fubm90YXRpb25zKA0KICAgICAgfnVuaXF1ZShGaWxtKSwgeCA9IDAuNSwgeSA9IDEsIA0KICAgICAgeHJlZiA9ICJwYXBlciIsIHlyZWYgPSAicGFwZXIiLCBzaG93YXJyb3cgPSBGQUxTRQ0KICAgICkNCn0NCg0KbG90ciAlPiUNCiAgc3BsaXQoLiRGaWxtKSAlPiUNCiAgbGFwcGx5KG9uZV9wbG90KSAlPiUgDQogIHN1YnBsb3QobnJvd3MgPSAxLCBzaGFyZVggPSBUUlVFLCB0aXRsZVggPSBGQUxTRSkgJT4lDQogIGhpZGVfbGVnZW5kKCkNCmBgYA0KDQoNCiMgR3JvdXBlZCBiYXIgcGxvdA0KYGBge3IgcGxvdGx5X2dyb3VwLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNn0NCnBsb3RfbHkobG90ciwgeCA9IH5SYWNlLCBjb2xvciA9IH5GaWxtKSAlPiUgYWRkX2hpc3RvZ3JhbSgpDQpgYGANCg0KIyBQbG90IG9mIHByb3BvcnRpb25zDQpgYGB7ciBwbG90bHlfcHJvcG9ydGlvbnMsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDZ9DQojIG51bWJlciBvZiBkaWFtb25kcyBieSBjdXQgYW5kIGNsYXJpdHkgKG4pDQpsb3RyX2NvdW50IDwtIGNvdW50KGxvdHIsIFJhY2UsIEZpbG0pDQojIG51bWJlciBvZiBkaWFtb25kcyBieSBjdXQgKG5uKQ0KbG90cl9wcm9wIDwtIGxlZnRfam9pbihsb3RyX2NvdW50LCBjb3VudChsb3RyX2NvdW50LCBSYWNlLCB3dCA9IG4pKQ0KbG90cl9wcm9wICU+JQ0KICBtdXRhdGUocHJvcCA9IG4gLyBubikgJT4lDQogIHBsb3RfbHkoeCA9IH5SYWNlLCB5ID0gfnByb3AsIGNvbG9yID0gfkZpbG0pICU+JQ0KICBhZGRfYmFycygpICU+JQ0KICBsYXlvdXQoYmFybW9kZSA9ICJzdGFjayIpDQpgYGANCg0KIyBZb3VyIFR1cm4NCjEuIFVzaW5nIHRoZSBgZ3NzX2NhdGAgZGF0YSwgY3JlYXRlIGEgaGlzdHJvZ3JhbSBmb3IgdGhlIGB0dmhvdXJzYCB2YXJpYWJsZS4gDQoyLiBVc2luZyB0aGUgYGdzc19jYXRgIGRhdGEsIGNyZWF0ZSBhIGJhciBjaGFydCBzaG93aW5nIHRoZSBgcGFydHlpZGAgdmFyaWFibGUgYnkgdGhlIGBtYXJpdGFsYCBzdGF0dXMuDQoNCiMgU2NhdHRlcnBsb3RzIGJ5IEhhbmQNCmBgYHtyIHBsb3RseV9zY2F0dGVyLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNiwgd2FybmluZyA9IEZBTFNFfQ0KcGxvdF9seShtaWR3ZXN0LCB4ID0gfnBvcGRlbnNpdHksIHkgPSB+cGVyY29sbGVnZSkgJT4lDQogIGFkZF9tYXJrZXJzKCkNCmBgYA0KDQojIENoYW5nZSBzeW1ib2wNCmBgYHtyIHBsb3RseV9zeW1ib2wsIGZpZy53aWR0aCA9IDksIGZpZy5oZWlnaHQgPSA2fQ0KcGxvdF9seShtaWR3ZXN0LCB4ID0gfnBvcGRlbnNpdHksIHkgPSB+cGVyY29sbGVnZSkgJT4lDQogIGFkZF9tYXJrZXJzKHN5bWJvbCA9IH5zdGF0ZSkNCmBgYA0KDQojIENoYW5nZSBjb2xvcg0KYGBge3IgcGxvdGx5X2NvbG9yLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gOX0NCnBsb3RfbHkobWlkd2VzdCwgeCA9IH5wb3BkZW5zaXR5LCB5ID0gfnBlcmNvbGxlZ2UpICU+JQ0KICBhZGRfbWFya2Vycyhjb2xvciA9IH5zdGF0ZSwgY29sb3JzID0gdmlyaWRpczo6dmlyaWRpcyg1KSkNCmBgYA0KDQojIExpbmUgR3JhcGgNCmBgYHtyIHBsb3RseV9saW5lLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gOX0NCnN0b3Jtc195ZWFybHkgPC0gc3Rvcm1zICU+JQ0KICBncm91cF9ieSh5ZWFyKSAlPiUNCiAgc3VtbWFyaXNlKG51bSA9IGxlbmd0aCh1bmlxdWUobmFtZSkpKQ0KDQpwbG90X2x5KHN0b3Jtc195ZWFybHksIHggPSB+eWVhciwgeSA9IH5udW0pICU+JQ0KICBhZGRfbGluZXMoKQ0KYGBgDQoNCiMgWW91ciBUdXJuDQoxLiANCjIuIA0KDQojIEhpZ2hjaGFydGVyOyBIaWdoY2hhcnRzIGZvciBSDQpgYGB7ciBpbnN0YWxsX2hpZ2hjaGFydGVyLCBldmFsID0gRkFMU0V9DQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImpia3Vuc3QvaGlnaGNoYXJ0ZXIiKQ0KYGBgDQoNCiMgYGhjaGFydGAgZnVuY3Rpb24NCmBgYHtyIGhjaGFydDEsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSA5fQ0KbGlicmFyeShoaWdoY2hhcnRlcikNCg0KbG90cl9jb3VudCA8LSBsb3RyICU+JQ0KICBjb3VudChGaWxtLCBSYWNlKQ0KDQpoY2hhcnQobG90cl9jb3VudCwgImNvbHVtbiIsIGhjYWVzKHggPSBSYWNlLCB5ID0gbiwgZ3JvdXAgPSBGaWxtKSkNCmBgYA0KDQojIEEgc2Vjb25kIGBoY2hhcnRgDQpgYGB7ciBoY2hhcnQyLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gOX0NCmhjaGFydChtaWR3ZXN0LCAic2NhdHRlciIsIGhjYWVzKHggPSBwb3BkZW5zaXR5LCB5ID0gcGVyY29sbGVnZSwgZ3JvdXAgPSBzdGF0ZSkpDQpgYGANCg0KIyBIaXN0b2dyYW0NCmBgYHtyIGhjaGFydF9oaXN0LCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gOX0NCmhjaGFydChsb3RyJFdvcmRzKQ0KYGBgDQoNCiMgWW91ciBUdXJuDQoxLiANCjIuIA0KDQojIEJ1aWxkIEhpZ2hjaGFydHMgZnJvbSBzY3JhdGNoDQpgYGB7ciBoY19zY3JhdGNoLCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNn0NCmhjIDwtIGhpZ2hjaGFydCgpICU+JQ0KICBoY194QXhpcyhjYXRlZ29yaWVzID0gbG90cl9jb3VudCRSYWNlKSAlPiUNCiAgaGNfYWRkX3NlcmllcyhuYW1lID0gJ1RoZSBGZWxsb3dzaGlwIE9mIFRoZSBSaW5nJywgDQogICAgICAgICAgICAgICAgZGF0YSA9IGZpbHRlcihsb3RyX2NvdW50LCBGaWxtID09ICdUaGUgRmVsbG93c2hpcCBPZiBUaGUgUmluZycpJG4pICU+JSANCiAgaGNfYWRkX3NlcmllcyhuYW1lID0gJ1RoZSBUd28gVG93ZXJzJywgDQogICAgICAgICAgICAgICAgZGF0YSA9IGZpbHRlcihsb3RyX2NvdW50LCBGaWxtID09ICdUaGUgVHdvIFRvd2VycycpJG4pICU+JQ0KICBoY19hZGRfc2VyaWVzKG5hbWUgPSAnVGhlIFJldHVybiBPZiBUaGUgS2luZycsIA0KICAgICAgICAgICAgICAgIGRhdGEgPSBmaWx0ZXIobG90cl9jb3VudCwgRmlsbSA9PSAnVGhlIFJldHVybiBPZiBUaGUgS2luZycpJG4pDQpoYw0KYGBgDQoNCiMgQ2hhbmdlIENoYXJ0IHR5cGUNCmBgYHtyIGhjX2NoYXJ0LCBmaWcud2lkdGggPSA5LCBmaWcuaGVpZ2h0ID0gNn0NCmhjIDwtIGhjICU+JQ0KICBoY19jaGFydCh0eXBlID0gJ2NvbHVtbicpDQpoYw0KYGBgDQoNCiMgQ2hhbmdlIENvbG9ycw0KYGBge3IgaGNfY2hhbmdlX2NvbG9ycywgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDZ9DQpoYyA8LSBoYyAlPiUNCiAgaGNfY29sb3JzKHN1YnN0cih2aXJpZGlzKDMpLCAwLCA3KSkNCmhjDQpgYGANCg0KIyBNb2RpZnkgQXhlcw0KYGBge3IgaGNfYXhpcywgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDl9DQpoYyA8LSBoYyAlPiUNCiAgaGNfeEF4aXModGl0bGUgPSBsaXN0KHRleHQgPSAiUmFjZSIpKSAlPiUNCiAgaGNfeUF4aXModGl0bGUgPSBsaXN0KHRleHQgPSAiTnVtYmVyIG9mIFdvcmRzIFNwb2tlbiIpLA0KICAgICAgICAgICBzaG93TGFzdExhYmVsID0gRkFMU0UpDQpoYw0KYGBgDQoNCiMgQWRkIHRpdGxlLCBzdWJ0aXRsZSwgbW92ZSBsZWdlbmQNCmBgYHtyIGhjX21vZGlmeSwgZmlnLmhlaWdodCA9IDYsIGZpZy53aWR0aCA9IDl9DQpoYyA8LSBoYyAlPiUNCiAgaGNfdGl0bGUodGV4dCA9ICdOdW1iZXIgb2YgV29yZHMgU3Bva2VuIGluIExvcmQgb2YgdGhlIFJpbmdzIEZpbG1zJywNCiAgICAgICAgICAgYWxpZ24gPSAnbGVmdCcpICU+JQ0KICBoY19zdWJ0aXRsZSh0ZXh0ID0gJ0Jyb2tlbiBkb3duIGJ5IDxpPkZpbG08L2k+IGFuZCA8Yj5SYWNlPC9iPicsIA0KICAgICAgICAgICAgICBhbGlnbiA9ICdsZWZ0JykgJT4lDQogIGhjX2xlZ2VuZChhbGlnbiA9ICdyaWdodCcsIHZlcnRpY2FsQWxpZ24gPSAndG9wJywgbGF5b3V0ID0gJ3ZlcnRpY2FsJywNCiAgICAgICAgICAgIHggPSAwLCB5ID0gODApICU+JQ0KICBoY19leHBvcnRpbmcoZW5hYmxlZCA9IFRSVUUpDQpoYw0KYGBgDQoNCg0KIyBZb3VyIFR1cm4NCjEuIA0KMi4gDQoNCiMgQ29ycmVsYXRpb24gTWF0cmljZXMNCmBgYHtyIGNvcnJlbGF0aW9ufQ0Kc2VsZWN0KHN0b3Jtcywgd2luZCwgcHJlc3N1cmUsIHRzX2RpYW1ldGVyLCBodV9kaWFtZXRlcikgJT4lDQogIGNvcih1c2UgPSAicGFpcndpc2UuY29tcGxldGUub2JzIikgJT4lDQogIGhjaGFydCgpDQpgYGANCg0KIyBMZWFmbGV0IEV4YW1wbGUNCmBgYHtyIGxlYWZsZXR9DQpsaWJyYXJ5KGxlYWZsZXQpDQoNCnN0b3JtcyAlPiUNCiAgZmlsdGVyKG5hbWUgJWluJSBjKCdJa2UnLCAnS2F0cmluYScpLCB5ZWFyID4gMjAwMCkgJT4lDQogIGxlYWZsZXQoKSAlPiUNCiAgYWRkVGlsZXMoKSAlPiUNCiAgYWRkQ2lyY2xlcyhsbmcgPSB+bG9uZywgbGF0ID0gfmxhdCwgcG9wdXAgPSB+bmFtZSwgd2VpZ2h0ID0gMSwNCiAgICAgICAgICAgICByYWRpdXMgPSB+d2luZCoxMDAwKQ0KYGBgDQoNCg0KIyBBZGRpdGlvbmFsIFJlc291cmNlcw0KKiBwbG90bHkgZm9yIFIgYm9vazogPGh0dHBzOi8vcGxvdGx5LWJvb2suY3BzaWV2ZXJ0Lm1lLz4NCiogcGxvdGx5OiA8aHR0cHM6Ly9wbG90Lmx5Lz4NCiogaGlnaGNoYXJ0ZXI6IDxodHRwOi8vamt1bnN0LmNvbS9oaWdoY2hhcnRlci9pbmRleC5odG1sPg0KKiBoaWdoY2hhcnRzOiA8aHR0cHM6Ly93d3cuaGlnaGNoYXJ0cy5jb20vPg0KKiBodG1sd2lkZ2V0czogPGh0dHBzOi8vd3d3Lmh0bWx3aWRnZXRzLm9yZy8+DQo=